package com.bjpowernode.config;

import com.bjpowernode.filter.CaptchaFilter;
import com.bjpowernode.handler.AppLogoutSuccessHandler;
import com.bjpowernode.handler.MyAuthenticationFailureHandler;
import com.bjpowernode.handler.MyAuthenticationSuccessHandler;
import org.apache.tomcat.util.file.ConfigurationSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

@EnableMethodSecurity   //开启方法上的权限检查
@Configuration
public class SecurityConfig {

    @Bean   //spring security的密码加密器，用于比较密码
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    public MyAuthenticationFailureHandler myAuthenticationFailureHandler;

    @Autowired
    public AppLogoutSuccessHandler appLogoutSuccessHandler;

    @Bean   //跨域配置源
    public CorsConfigurationSource configurationSource(){
        //跨域配置
        CorsConfiguration config = new CorsConfiguration();
        //这里的set的配置方法跟下面的add配置方法差不多
        config.setAllowedOrigins(Arrays.asList("*"));
        config.setAllowedMethods(Arrays.asList("*"));
        config.setAllowedHeaders(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source =
                new UrlBasedCorsConfigurationSource();
        //注册跨域配置，下面这个/**表示访问后端任何接口，都用config进行配置
        source.registerCorsConfiguration("/**",config);
        return source;
    }

    //配置spring security框架的一些行为(配置我们自己的登录页，不适用框架默认的登录页)
    //但是当配置了SecurityFilterChain这个Bean之后，security框架的某些默认行为就失效了
    @Bean   //安全过滤器链Bean        自动注入HttpSecurity
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity,
            CorsConfigurationSource configurationSource) throws Exception {
        return httpSecurity
                //配置我们自己的登录页
                .formLogin(fl -> {
                    //security框架默认接收登录提交请求的地址是/login，也就是表单提交的地址，
                    //但是我们自定义配置之后，这些默认行为失效了，所以下面loginProcessingUrl
                    //把它给加了回来。就是登录账号和密码往那个地址提交
                    fl.loginProcessingUrl("/login")
                            //登录成功后因为默认是跳/根路径，所以这里要给前端返回指定信息话就要指定handler
                            .successHandler(myAuthenticationSuccessHandler)
                            .failureHandler(myAuthenticationFailureHandler);//登录失败则跳到这个handler
                    //由于是前端提交的/login请求，且是前端访问，所以没有前一个路径，并且这里
                    //没有配置登录成功后默认跳转的路径，所以这里登录成功后默认跳转到根路径/
                })
                .logout(logout -> {
                    //设置退出登录请求提交到这个地址
                    logout.logoutUrl("/logout")
                            //退出成功后会把security框架中的上下文的认证信息清理掉，下面的上下文
                            //SecurityContextHolder.getContext().getAuthentication
                            .logoutSuccessHandler(appLogoutSuccessHandler);
                })
                //把拦截所有接口请求进行登录状态检查的默认行为加回来
                .authorizeHttpRequests(auth -> {
                    //anyRequest().authenticated()表示任何对后端接口的请求，
                    //都需要登录认证后才能访问。要注意的是，这个配置只会拦截所有controller中的请求，
                    //security框架中配置的请求不会拦截，像登录和登出都不会拦截
                    auth.anyRequest().authenticated();
                })
//                .csrf(AbstractHttpConfigurer::disable)直接用方法引用禁用
                .csrf(csrf -> {
                    //禁用csrf跨站请求伪造，禁用之后，就不安全了，有csrf网络攻击的风险，
                    //后面用jwt验证身份解决。这个csrf就是之前那个前端模板文件的隐藏域用的
                    csrf.disable();
                })
                .cors(cors -> { //配置跨域解决方式
                    cors.configurationSource(configurationSource);
                })
                .sessionManagement( management -> {
                    //session创建策略(无session状态)，让security框架不再创建session
                    management.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })
                .build();
    }

    //跨域过滤器
//    @Bean
//    public CorsFilter corsFilter(){
//        CorsConfiguration config = new CorsConfiguration();
//        //允许白名单域名进行跨域调用，允许哪些请求进行跨域
//        config.addAllowedOrigin("*");
//        //允许所有请求方法进行跨域调用
//        config.addAllowedMethod("*");
//        //允许所有请求头进行跨域，用于解决修改请求头的复杂跨域
//        config.addAllowedHeader("*");
//        //允许跨域发送cookie
//        config.setAllowCredentials(true);
//        UrlBasedCorsConfigurationSource source =
//                new UrlBasedCorsConfigurationSource();
//        //这个/**表示所有的请求都要经过这个过滤器
//        source.registerCorsConfiguration("/**",config);
//        return new CorsFilter(source);
//    }
}
